﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace Tradex.Market {

    /// <summary>
    /// The Activity is an activity on a market for an instrument - either a trade, or a change in bids or asks.
    /// </summary>
    [Serializable]
    public sealed class Activity {

        /// <summary>
        /// Internal constructo for the factory methods.
        /// </summary>
        /// <param name="timestamp"></param>
        /// <param name="type"></param>
        private Activity(DateTime timestamp, ActivityType type) {
            Timestamp = timestamp;
            ActivityType = type;
        }

        /// <summary>
        /// Constructor for a market mode change.
        /// </summary>
        /// <param name="timestamp"></param>
        /// <param name="type"></param>
        /// <param name="mode"></param>
        /// <param name="modeCode"></param>
        public Activity(DateTime timestamp, ActivityType type, MarketModes mode, string modeCode) {
            ActivityType = type;
            Mode = mode;
            ModeCode = modeCode;
            Timestamp = timestamp;
            Price = 0;
            Volume = 0;
        }

        /// <summary>
        /// Constructor taking a price.
        /// </summary>
        /// <param name="timestamp"></param>
        /// <param name="type"></param>
        /// <param name="price"></param>
        public Activity(DateTime timestamp, ActivityType type, Double price) {
            ActivityType = type;
            Mode = MarketModes.None;
            Timestamp = timestamp;
            Price = price;
            Volume = 0;
        }

        /// <summary>
        /// Constructor taking volume.
        /// </summary>
        /// <param name="timestamp"></param>
        /// <param name="type"></param>
        /// <param name="volume"></param>
        public Activity(DateTime timestamp, ActivityType type, UInt64 volume) {
            ActivityType = type;
            Mode = MarketModes.None;
            Timestamp = timestamp;
            Price = 0;
            Volume = volume;
        }

        /// <summary>
        /// Constructor taking price and volume.
        /// </summary>
        /// <param name="timestamp"></param>
        /// <param name="type"></param>
        /// <param name="price"></param>
        /// <param name="volume"></param>
        public Activity(DateTime timestamp, ActivityType type, Double price, UInt64 volume) {
            ActivityType = type;
            Mode = MarketModes.None;
            Timestamp = timestamp;
            Price = price;
            Volume = volume;
        }

        /// <summary>
        /// Constructo traking all properties.
        /// </summary>
        /// <param name="type"></param>
        /// <param name="mode"></param>
        /// <param name="timestamp"></param>
        /// <param name="price"></param>
        /// <param name="volume"></param>
        public Activity(ActivityType type, MarketModes mode, DateTime timestamp, Double price, UInt64 volume) {
            ActivityType = type;
            Mode = mode;
            Timestamp = timestamp;
            Price = price;
            Volume = volume;
        }

        /// <summary>
        /// Gets the type of activity reported.
        /// </summary>
        public ActivityType ActivityType { get; private set; }

        /// <summary>
        /// Gets the new MarketModes, in Tradex standardized form.
        /// </summary>
        public MarketModes Mode { get; private set; }

        /// <summary>
        /// Gets a string representation of the new market mode, as transmitted by the exchange.
        /// </summary>
        public string ModeCode { get; private set; }

        /// <summary>
        /// Gets the Timestamp of the entry.
        /// </summary>
        public DateTime Timestamp { get; private set; }

        /// <summary>
        /// Gets the Price
        /// </summary>
        public Double Price { get; private set; }

        /// <summary>
        /// Gets the Volume
        /// </summary>
        public UInt64 Volume { get; private set; }

        /// <summary>
        /// Gets the seuqnce code. This code is only provided by some feeds and only for trades and is the
        /// exchange side identifier of the trade. It is used to idenfity reference points
        /// for update and cancel messages (i.e. cancel trade with sequence xxx).
        /// </summary>
        public UInt32 Sequence { get; private set; }

        /// <inheritdoc />
        public override string ToString() {
            string retval = null;
            switch (ActivityType) {
                case ActivityType.Mode:
                    retval = String.Format (
                        CultureInfo.InvariantCulture, "{0:yyyy-MM-dd HH:mm:ss.fffffff} {1} {2} / {3}",
                        Timestamp, ActivityType, Mode, ModeCode
                    );
                    break;
                case ActivityType.Volume:
                    retval = String.Format (
                        CultureInfo.InvariantCulture, "{0:yyyy-MM-dd HH:mm:ss.fffffff} {1} {2}",
                        Timestamp, ActivityType, Volume
                    );
                    break;
                case ActivityType.Close:
                case ActivityType.Open:
                case ActivityType.High:
                case ActivityType.Low:
                case ActivityType.Settlement:
                case ActivityType.Closing:
                case ActivityType.Opening:
                    retval = String.Format (
                        CultureInfo.InvariantCulture, "{0:yyyy-MM-dd HH:mm:ss.fffffff} {1} {2}",
                        Timestamp, ActivityType, Price
                    );
                    break;
                case ActivityType.Trade:
                case ActivityType.BestBid:
                case ActivityType.BestAsk:
                case ActivityType.Ask:
                case ActivityType.Bid:
                    if (Sequence == 0) {
                        retval = String.Format(
                            CultureInfo.InvariantCulture, "{0:yyyy-MM-dd HH:mm:ss.fffffff} {1} {2} @ {3}",
                            Timestamp, ActivityType, Volume, Price
                        );
                    } else {
                        retval = String.Format(
                            CultureInfo.InvariantCulture, "{0:yyyy-MM-dd HH:mm:ss.fffffff} {1} {2} @ {3} ({4})",
                            Timestamp, ActivityType, Volume, Price, Sequence
                        );
                    }
                    break;
            }
            return retval;
        }

        #region "Statics"

        /// <summary>
        /// Constructor for an activity of ActivityType "Trade".
        /// </summary>
        /// <param name="timestamp"></param>
        /// <param name="price"></param>
        /// <param name="volume"></param>
        /// <param name="sequence"></param>
        /// <returns></returns>
        public static Activity ForTrade (DateTime timestamp, double price, uint volume, uint sequence) {
            Activity retval = new Activity(timestamp, ActivityType.Trade) {
                Price = price,
                Volume = volume,
                Sequence = sequence,
            };
            return retval;
        }

        #endregion "Statics"
    }

}
